package leetcode.concurrency;

import java.util.concurrent.Semaphore;
import java.util.function.IntConsumer;

/**
 * 交替打印字符串
 * @author jiangwz
 * @create 2019/11/7.
 */
public class Q1195 {

    public static void main(String[] args){
        FizzBuzz fizzBuzz = new FizzBuzz(15);
        IntConsumer consumer = value -> System.out.print(value+" ");
        new Thread(() -> {
            try {
                fizzBuzz.fizz(()-> System.out.print("fizz "));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread-name1").start();
        new Thread(() -> {
            try {
                fizzBuzz.buzz(()-> System.out.print("buzz "));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread-name2").start();
        new Thread(() -> {
            try {
                fizzBuzz.fizzbuzz(()-> System.out.print("fizzbuzz "));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread-name3").start();
        new Thread(() -> {
            try {
                fizzBuzz.number(consumer);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread-name4").start();
    }
}

// 阻塞队列
//执行用时 :
//        5 ms
//        , 在所有 java 提交中击败了
//        99.39%
//        的用户
//        内存消耗 :
//        36 MB
//        , 在所有 java 提交中击败了
//        100.00%
//        的用户
//class FizzBuzz {
//
//    BlockingQueue<Integer> number = new LinkedBlockingQueue<>();
//    BlockingQueue<Integer> fizz = new LinkedBlockingQueue<>();
//    BlockingQueue<Integer> buzz = new LinkedBlockingQueue<>();
//    BlockingQueue<Integer> fizzbuzz = new LinkedBlockingQueue<>();
//
//    private int n;
//
//    public FizzBuzz(int n) {
//        this.n = n;
//    }
//
//    // printFizz.run() outputs "fizz".
//    public void fizz(Runnable printFizz) throws InterruptedException {
//        for (int i = 1; i <= n; i++) {
//            if (i%3==0 && i%5!=0){
//                fizz.take();
//                printFizz.run();
//                number.add(1);
//            }
//        }
//    }
//
//    // printBuzz.run() outputs "buzz".
//    public void buzz(Runnable printBuzz) throws InterruptedException {
//        for (int i = 1; i <= n; i++) {
//            if (i%5==0 && i%3!=0){
//                buzz.take();
//                printBuzz.run();
//                number.add(1);
//            }
//        }
//    }
//
//    // printFizzBuzz.run() outputs "fizzbuzz".
//    public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
//        for (int i = 1; i <= n; i++) {
//            if (i%5==0 && i%3==0){
//                fizzbuzz.take();
//                printFizzBuzz.run();
//                number.add(1);
//            }
//        }
//    }
//
//    // printNumber.accept(x) outputs "x", where x is an integer.
//    public void number(IntConsumer printNumber) throws InterruptedException {
//        number.add(1);
//        for (int i = 1; i <= n; i++) {
//            number.take();
//            if (i%3==0 && i%5!=0) {
//                fizz.add(1);
//                continue;
//            }
//            else if (i%5==0 && i%3!=0){
//                buzz.add(1);
//                continue;
//            }
//            else if (i%5==0 && i%3==0) {
//                fizzbuzz.add(1);
//                continue;
//            }else {
//                number.add(1);
//                printNumber.accept(i);
//            }
//        }
//    }
//}


//信号量实现
//执行用时 :
//        5 ms
//        , 在所有 java 提交中击败了
//        99.39%
//        的用户
//        内存消耗 :
//        35.9 MB
//        , 在所有 java 提交中击败了
//        100.00%
//        的用户
class FizzBuzz {

    Semaphore fizz = new Semaphore(0);
    Semaphore buzz = new Semaphore(0);
    Semaphore fizzbuzz = new Semaphore(0);
    Semaphore number = new Semaphore(0);

    private int n;

    public FizzBuzz(int n) {
        this.n = n;
    }

    // printFizz.run() outputs "fizz".
    public void fizz(Runnable printFizz) throws InterruptedException {
        for (int i = 1; i <= n; i++) {
            if (i%3==0 && i%5!=0){
                fizz.acquire();
                printFizz.run();
                number.release();
            }
        }
    }

    // printBuzz.run() outputs "buzz".
    public void buzz(Runnable printBuzz) throws InterruptedException {
        for (int i = 1; i <= n; i++) {
            if (i%5==0 && i%3!=0){
                buzz.acquire();
                printBuzz.run();
                number.release();
            }
        }
    }

    // printFizzBuzz.run() outputs "fizzbuzz".
    public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
        for (int i = 1; i <= n; i++) {
            if (i%5==0 && i%3==0){
                fizzbuzz.acquire();
                printFizzBuzz.run();
                number.release();
            }
        }
    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void number(IntConsumer printNumber) throws InterruptedException {
        number.release();
        for (int i = 1; i <= n; i++) {
            number.acquire();
            if (i%3==0 && i%5!=0) {
                fizz.release();
                continue;
            }
            else if (i%5==0 && i%3!=0){
                buzz.release();
                continue;
            }
            else if (i%5==0 && i%3==0) {
                fizzbuzz.release();
                continue;
            }else {
                number.release();
                printNumber.accept(i);
            }
        }
    }
}
